<?php

namespace services\task;

use Yii;
use common\components\Service;
use common\helpers\DingHelper;
use common\models\base\PatientDiseases;
use common\models\clinic\Clinic;
use common\models\clinic\Project as ClinicProject;
use common\models\clinic\UserSurplus;
use common\models\marketing\OutpatientType;
use common\models\marketing\Tags;
use common\models\marketing\SourceChannel;
use common\models\member\Member;
use common\models\member\Attr;
use common\models\task\Task;
use common\models\task\TaskCondition;
use common\models\task\TaskMember;
use common\models\task\TaskOperateLog;
use common\models\task\TaskPatient;
use common\models\task\TaskRemind;
use common\models\patient\User;
use common\models\patient\Callback;
use common\models\patient\Tags as PatientTags;
use common\helpers\CommonHelper;
use api\modules\v1\forms\task\TaskForm;
use services\common\CommonService;
use services\patient\UserService;

/**
 * Class TaskService
 * @package services\task
 */
class TaskService extends Service
{
    /**
     * 任务列表
     *
     * @param array $params
     * @param int $page
     * @param int $pageSize
     * @param int $directTotal
     * @return array
     */
    public function getListPage(array $params = [], int $page = 0, int $pageSize = 0): array
    {
        $memberList = CommonService::getMemberList(3);
        $memberIds = array_merge(array_column($memberList, 'id'), [Yii::$app->user->identity->member_id]);

        $query = Task::find()
            ->where([
                'project_id' => Yii::$app->params['project_id'],
                'created_member_id' => $memberIds,
            ])
            ->andWhere(['!=', 'status', Task::STATUS_DELETE]);

        if (isset($params['status']) && is_numeric($params['status']) && $params['status'] >= 0) {
            switch ((int)$params['status']) {
                case Task::STATUS_WAIT:// 未开始【未开始 && 开始时间 > 当前时间】
                    $query->andWhere(['status' => Task::STATUS_WAIT]);
                    $query->andWhere(['>', 'start_time', Yii::$app->params['time']]);
                    break;
                case Task::STATUS_START:// 进行中【未开始+进行中 && 开始时间 <= 当前时间 <= 结束时间】
                    $query->andWhere([
                        'status' => [Task::STATUS_WAIT, Task::STATUS_START],
                    ]);
                    $query->andWhere(['<=', 'start_time', Yii::$app->params['time']]);
                    $query->andWhere(['>=', 'end_time', Yii::$app->params['time'] - 86400]);
                    break;
                case Task::STATUS_DELAY:// 已延期【未开始+进行中+已延期 && 结束时间 < 当前时间】
                    $query->andWhere([
                        'status' => [Task::STATUS_WAIT, Task::STATUS_START, Task::STATUS_DELAY]
                    ]);
                    $query->andWhere(['<', 'end_time', Yii::$app->params['time'] - 86400]);
                    break;
                case Task::STATUS_SUCCESS:// 已完成
                case Task::STATUS_CLOSE:// 已关闭
                default:
                    $query->andWhere(['status' => $params['status']]);
                    break;
            }
        }

        if (isset($params['start_time']) && $params['start_time']) {
            $query->andWhere(['>=', 'created_at', strtotime($params['start_time'])]);
        }
        if (isset($params['end_time']) && $params['end_time']) {
            $query->andWhere(['<=', 'created_at', strtotime($params['end_time'].' 23:59:59')]);
        }

        $order = [
            'created_at' => SORT_DESC,
            'id' => SORT_DESC,
        ];

        $data = $this->getListPageRes($query, $page, $pageSize, $order);

        // 格式化数据
        $data['list'] = $this->getListFormat($data['list']);

        return $data;
    }

    /**
     * 列表数据格式化
     *
     * @param array $data
     * @return array
     */
    public function getListFormat(array $data = []): array
    {
        // 任务人
        $taskMemberService = new TaskMemberService();
        $taskMemberList = $taskMemberService->getTaskMemberList([
            'task_id' => array_column($data, 'id'),
        ]);

        /**
         * @var Member[] $memberList
         */
        $memberList = Member::find()
            ->where(['id' => array_column($data, 'created_member_id')])
            ->indexBy('id')
            ->all();

        $list = [];
        foreach ($data as $row) {
            // 任务人
            $taskMemberArr = $taskMemberList[$row['id']]['member_name_arr'] ?? [];

            // 状态修复为定义状态
            $status = $this->getTrueStatus((int)$row['status'], $row['start_time'], $row['end_time']);

            // 完成率
            $rate = $this->getTaskRate($row['callback_num'], $row['total_num']);

            $list[] = [
                'id' => $row['id'],
                'project_id' => $row['project_id'],
                'title' => $row['title'],
                'start_time_str' => date('Y-m-d', $row['start_time']),
                'end_time_str' => date('Y-m-d', $row['end_time']),
                'callback_num' => $row['callback_num'],
                'total_num' => $row['total_num'],
                'rate' => $rate,
                'member_name_arr' => $taskMemberArr,
                'remark' => $row['remark'],
                'status' => $status,
                'status_str' => Task::$statuss[$status],
                'created_name' => $memberList[$row['created_member_id']]->realname,
                'created_at_str' => date('Y-m-d H:i', $row['created_at']),
            ];
        }

        return $list;
    }

    /**
     * 任务信息
     *
     * @param int $id
     * @return array
     */
    public function getInfo(int $id = 0): array
    {
        $task = Task::findOne($id);
        if (empty($task)) {
            return CommonHelper::json(422, '任务不存在');
        }
        if ($task->project_id != Yii::$app->params['project_id']) {
            return CommonHelper::json(422, '任务非当前项目');
        }

        // 任务人
        $taskMemberService = new TaskMemberService();
        $taskMemberList = $taskMemberService->getTaskMemberList([
            'task_id' => $task->id,
        ]);
        $taskMemberArr = $taskMemberList[$task->id]['member_name_arr'] ?? [];

        // 创建人
        $createdMember = Member::findOne($task->created_member_id);

        // 状态修复为定义状态
        $status = $this->getTrueStatus((int)$task->status, $task->start_time, $task->end_time);

        // 完成率
        $rate = $this->getTaskRate($task->callback_num, $task->total_num);

        // 筛选条件
        $taskCondition = TaskCondition::findOne([
            'task_id' => $task->id,
        ]);
        // 就诊类型
        $outpatientNameArr = OutpatientType::find()
            ->select(['outpatient_name'])
            ->where(['id' => $taskCondition->outpatient_type_ids])
            ->column();
        // 标签
        $tagNameArr = Tags::find()
            ->select(['tags_name'])
            ->where([
                'id' => $taskCondition->tag_ids
            ])
            ->column();
        // 病种
        $diseasesNameArr = PatientDiseases::find()
            ->select(['diseases_name'])
            ->where([
                'id' => $taskCondition->patient_diseases_ids
            ])
            ->column();

        // 任务日志
        $taskLogService = new TaskOperateLogService();
        $taskLogList = $taskLogService->getTaskLogList($task->id);

        // 操作控制
        $isEditOp = $isCloseOp = 0;
        $isDeleteOp = 1;
        // 编辑操作【未开始】
        if ($status == Task::STATUS_WAIT) {
            $isEditOp = 1;
        }
        // 关闭操作【进行中、已延期】
        if (in_array($status, [Task::STATUS_START, Task::STATUS_DELAY])) {
            $isCloseOp = 1;
        }

        return [
            'id' => $task->id,
            'project_id' => $task->project_id,
            'title' => $task->title,
            'start_time' => date('Y-m-d', $task->start_time),
            'end_time' => date('Y-m-d', $task->end_time),
            'callback_num' => $task->callback_num,
            'total_num' => $task->total_num,
            'rate' => $rate,
            'member_name_arr' => $taskMemberArr,
            'status' => $status,
            'status_str' => Task::$statuss[$status],
            'created_name' => $createdMember->realname,
            'created_at_str' => date('Y-m-d H:i', $task->created_at),
            'remark' => $task->remark,
            'select_condition' => [
                'start_reg_time' => $taskCondition->start_reg_time ? date('Y-m-d', $taskCondition->start_reg_time) : '',
                'end_reg_time' => $taskCondition->end_reg_time ? date('Y-m-d', $taskCondition->end_reg_time) : '',
                'start_last_callback_time' => $taskCondition->start_last_callback_time ? date('Y-m-d', $taskCondition->start_last_callback_time) : '',
                'end_last_callback_time' => $taskCondition->end_last_callback_time ? date('Y-m-d', $taskCondition->end_last_callback_time) : '',
                'start_missed_days' => $taskCondition->start_missed_days,
                'end_missed_days' => $taskCondition->end_missed_days,
                'start_surplus_num' => $taskCondition->start_surplus_num,
                'end_surplus_num' => $taskCondition->end_surplus_num,
                'outpatient_name_arr' => $outpatientNameArr,
                'tag_name_arr' => $tagNameArr,
                'diseases_name_arr' => $diseasesNameArr,
                'is_anti_relapse' => $taskCondition->is_anti_relapse,
            ],
            'operate_log_list' => $taskLogList,
            'is_edit_op' => $isEditOp,// 编辑【1是、0否】
            'is_close_op' => $isCloseOp,// 关闭【1是、0否】
            'is_delete_op' => $isDeleteOp,// 删除【1是、0否】
        ];
    }

    /**
     * 编辑任务信息
     *
     * @param int $id
     * @return array
     */
    public function getEditInfo(int $id = 0): array
    {
        $task = Task::findOne($id);
        if ($task && $task->project_id != Yii::$app->params['project_id']) {
            return CommonHelper::json(422, '任务非当前项目');
        }

        // 任务人
        $taskMemberIdArr = $id ? TaskMember::find()
            ->select(['member_id'])
            ->where([
                'task_id' => $id
            ])
            ->column() : [];

        // 筛选条件
        $taskCondition = TaskCondition::findOne([
            'task_id' => $id,
        ]);
        $condition = [
            'start_reg_time' => $taskCondition && $taskCondition->start_reg_time ? date('Y-m-d', $taskCondition->start_reg_time) : '',
            'end_reg_time' => $taskCondition && $taskCondition->end_reg_time ? date('Y-m-d', $taskCondition->end_reg_time) : '',
            'start_last_callback_time' => $taskCondition && $taskCondition->start_last_callback_time ? date('Y-m-d', $taskCondition->start_last_callback_time) : '',
            'end_last_callback_time' => $taskCondition && $taskCondition->end_last_callback_time ?date('Y-m-d', $taskCondition->end_last_callback_time) : '',
            'start_missed_days' => $taskCondition->start_missed_days ?? '',
            'end_missed_days' => $taskCondition->end_missed_days ?? '',
            'start_surplus_num' => $taskCondition->start_surplus_num ?? '',
            'end_surplus_num' => $taskCondition->end_surplus_num ?? '',
            'outpatient_type_ids' => $taskCondition->outpatient_type_ids ?? [],
            'tag_ids' => $taskCondition->tag_ids ?? [],
            'patient_diseases_ids' => $taskCondition->patient_diseases_ids ?? [],
            'is_anti_relapse' => $taskCondition->is_anti_relapse ?? 0,
        ];
//        if ($taskCondition) {
            $patientData = $this->selectTaskPatientList($condition, 1);
//        }

        return [
            'id' => $task->id ?? 0,
            'project_id' => $task->project_id ?? 0,
            'title' => $task->title ?? '',
            'start_time' => $task ? date('Y-m-d', $task->start_time) : '',
            'end_time' => $task ? date('Y-m-d', $task->end_time) : '',
            'total_num' => $patientData['total'] ?? 0,// 重新获取总人数
            'member_id_arr' => $taskMemberIdArr,
            'remark' => $task->remark ?? '',

            // 患者筛选条件
            'select_condition' => $condition,

            // 筛选条件下拉框
            'condition' => [
                'tags_list' => CommonService::getTagsList(),
                'outpatient_type_list' => CommonService::getOutpatientTypeList(0),
                'patient_diseases_list' => CommonService::getDiseasesList(),
                'member_list' => CommonService::getMemberList(3, 1),
            ],
        ];
    }

    /**
     * 筛选任务患者
     *
     * @param array $params
     * @param int $directTotal 是否直接返回总数
     * @return array
     */
    public function selectTaskPatientList(array $params = [], int $directTotal = 0): array
    {
        $query = User::find()
            ->select(['id'])
            ->where([
                'project_id' => Yii::$app->params['project_id'],
                'status' => [User::STATUS_INVALID, User::STATUS_ENABLE]
            ]);

        // 挂号时间
        if (isset($params['start_reg_time']) && $params['start_reg_time']) {
            $query->andWhere(['>=', 'reg_time', strtotime($params['start_reg_time'])]);
        }
        if (isset($params['end_reg_time']) && $params['end_reg_time']) {
            $query->andWhere(['<=', 'reg_time', strtotime($params['end_reg_time'].' 23:59:59')]);
        }
        // 挂号时间
        if ((isset($params['start_last_callback_time']) && $params['start_last_callback_time'])
            && (isset($params['end_last_callback_time']) && $params['end_last_callback_time'])) {
            $query->andWhere([
                'or',
                [
                    'and',
                    ['>=', 'kf_last_callback_time', strtotime($params['start_last_callback_time'])],
                    ['<=', 'kf_last_callback_time', strtotime($params['end_last_callback_time'].' 23:59:59')],
                ],
                [
                    'and',
                    ['>=', 'yz_last_callback_time', strtotime($params['start_last_callback_time'])],
                    ['<=', 'yz_last_callback_time', strtotime($params['end_last_callback_time'].' 23:59:59')],
                ],
            ]);
        }
        // 就诊类型
        if (isset($params['outpatient_type_ids']) && $params['outpatient_type_ids']) {
            $outpatientTypeIds = is_array($params['outpatient_type_ids']) ? $params['outpatient_type_ids'] : explode(',', $params['outpatient_type_ids']);
            $query->andWhere(['outpatient_type_id' => $outpatientTypeIds]);
        }
        // 标签
        if (isset($params['tag_ids']) && $params['tag_ids']) {
            $tagIds = is_array($params['tag_ids']) ? $params['tag_ids'] : explode(',', $params['tag_ids']);

            // 患者IDs【绑定标签患者】
            $patientUserIds = PatientTags::find()
                ->select(['patient_user_id'])
                ->where([
                    'tags_id' => $tagIds,
                    'status' => PatientTags::STATUS_ENABLE,
                ])
                ->column();
            $query->andWhere(['id' => array_unique($patientUserIds)]);
        }
        // 病种【一级】
        if (isset($params['patient_diseases_ids']) && $params['patient_diseases_ids']) {
            $patientDiseasesIds = is_array($params['patient_diseases_ids']) ? $params['patient_diseases_ids'] : explode(',', $params['patient_diseases_ids']);

            $query->andWhere(['patient_diseases_id' => $patientDiseasesIds]);
        }
        // 未到天数
        if (isset($params['start_missed_days']) && is_numeric($params['start_missed_days'])) {
            $query->andWhere(['>=', 'missed_days', $params['start_missed_days']]);
        }
        if (isset($params['end_missed_days']) && is_numeric($params['end_missed_days'])) {
            $query->andWhere(['<=', 'missed_days', $params['end_missed_days']]);
        }
        // 308剩余数
        if ((isset($params['start_surplus_num']) && is_numeric($params['start_surplus_num']))
            || (isset($params['end_surplus_num']) && is_numeric($params['end_surplus_num']))) {
            $clinicSurplusQuery = UserSurplus::find()->alias('us')
                ->select(['patient_card_id', 'SUM(us.total_num + us.help_num) surplus_num'])
                ->innerJoin(ClinicProject::tableName().' cp', 'cp.id = us.clinic_project_id')
                ->where([
                    'us.project_id' => Yii::$app->params['project_id'],
                    'cp.template' => ClinicProject::TEMPLATE_308,
                    'cp.status' => [ClinicProject::STATUS_INVALID, ClinicProject::STATUS_ENABLE]
                ])
                ->groupBy('patient_card_id');

            if (isset($params['start_surplus_num']) && is_numeric($params['start_surplus_num'])) {
                $clinicSurplusQuery->having(['>=', 'surplus_num', $params['start_surplus_num']]);
            }

            if (isset($params['end_surplus_num']) && is_numeric($params['end_surplus_num'])) {
                $clinicSurplusQuery->having(['<=', 'surplus_num', $params['end_surplus_num']]);
            }
            $clinicSurplusList = $clinicSurplusQuery->asArray()->all();

            $query->andWhere(['patient_card_id' => array_column($clinicSurplusList, 'patient_card_id')]);
        }
        // 抗复发标识
        if (isset($params['is_anti_relapse']) && $params['is_anti_relapse']) {
            $query->andWhere(['is_anti_relapse' => $params['is_anti_relapse']]);
        }

        return $this->getListPageRes($query, 0, 0, [], $directTotal);
    }

    /**
     * 获取实际状态
     *
     * @param int $status 状态【Task::STATUS_**】
     * @param int $startTime 开始时间
     * @param int $endTime 结束时间
     * @return int
     */
    public function getTrueStatus(int $status, int $startTime, int $endTime): int
    {
        // 状态修复为定义状态
        switch ($status) {
            case Task::STATUS_WAIT:// 未开始【未开始 && 开始时间 > 当前时间】
                if ($endTime < Yii::$app->params['time'] - 86400) {
                    $status = Task::STATUS_DELAY;
                } elseif ($startTime <= Yii::$app->params['time']) {
                    $status = Task::STATUS_START;
                }
                break;
            case Task::STATUS_START:// 进行中【未开始+进行中 && 开始时间 <= 当前时间 <= 结束时间】
                if ($endTime < Yii::$app->params['time'] - 86400) {
                    $status = Task::STATUS_DELAY;
                }
                break;
            case Task::STATUS_DELAY:// 已延期
            case Task::STATUS_SUCCESS:// 已完成
            case Task::STATUS_CLOSE:// 已关闭
            default:
                break;
        }

        return $status;
    }

    /**
     * 获取任务完成率
     *
     * @param int $callbackNum 已回访人数
     * @param int $totalNum 总人数
     * @return int|string
     */
    public function getTaskRate(int $callbackNum, int $totalNum)
    {
        // 完成率
        $rate = $totalNum > 0 ? number_format($callbackNum / $totalNum * 100, 2, '.', '') : 0;

        return $rate;
    }

    /**
     * 检验筛选参数是否设置
     *
     * @param array $params
     * @return bool
     * @throws \Exception
     */
    public function checkPatientCondition(array $params = [])
    {
        $key = [
            'start_reg_time', 'end_reg_time', 'start_last_callback_time', 'end_last_callback_time', 'start_missed_days', 'end_missed_days', 'start_surplus_num', 'end_surplus_num', 'outpatient_type_ids', 'tag_ids', 'patient_diseases_ids', 'is_anti_relapse'
        ];

        $isRequire = 0;
        foreach ($key as $row) {
            if ($row == 'is_anti_relapse') {
                continue;
            }

            if (isset($params[$row])) {
                if (in_array($row, ['outpatient_type_ids', 'tag_ids', 'patient_diseases_ids'])) {
                    $data = is_array($params[$row]) ? $params[$row] : explode(',', $params[$row]);
                    if (array_filter($data)) {
                        $isRequire = 1;
                        break;
                    }
                } else {
                    if (in_array($row, ['start_missed_days', 'end_missed_days', 'start_surplus_num', 'end_surplus_num'])) {
                        if (is_numeric($params[$row])) {
                            $isRequire = 1;
                            break;
                        }
                    } elseif ($params[$row]) {
                        $isRequire = 1;
                        break;
                    }
                }
            }
        }

        if (empty($isRequire)) {
            throw new \Exception('任务患者筛选项至少填一个');
        }

        return true;
    }

    /**
     * 我的任务列表【工作台】
     *
     * @param array $params
     * @param int $page
     * @param int $pageSize
     * @param int $directTotal
     * @return array
     */
    public function getMyList(array $params = [], int $page = 0, int $pageSize = 0, int $directTotal = 0): array
    {
        $query = Task::find()->alias('t')
            ->select(['t.*'])
            ->innerJoin(TaskMember::tableName().' tm', 'tm.task_id = t.id')
            ->where([
                'tm.member_id' => Yii::$app->user->identity->member_id,
            ])
            ->andWhere(['!=', 't.status', Task::STATUS_DELETE]);

        if (isset($params['status']) && is_numeric($params['status']) && $params['status'] >= 0) {
            switch ((int)$params['status']) {
                case Task::STATUS_WAIT:// 未开始【未开始 && 开始时间 > 当前时间】
                    $query->andWhere(['t.status' => Task::STATUS_WAIT]);
                    $query->andWhere(['>', 't.start_time', Yii::$app->params['time']]);
                    break;
                case Task::STATUS_START:// 进行中【未开始+进行中 && 开始时间 <= 当前时间 <= 结束时间】
                    $query->andWhere([
                        't.status' => [Task::STATUS_WAIT, Task::STATUS_START],
                    ]);
                    $query->andWhere(['<=', 't.start_time', Yii::$app->params['time']]);
                    $query->andWhere(['>=', 't.end_time', Yii::$app->params['time'] - 86400]);
                    break;
                case Task::STATUS_DELAY:// 已延期【未开始+进行中+已延期 && 结束时间 < 当前时间】
                    $query->andWhere([
                        't.status' => [Task::STATUS_WAIT, Task::STATUS_START, Task::STATUS_DELAY]
                    ]);
                    $query->andWhere(['<', 't.end_time', Yii::$app->params['time'] - 86400]);
                    break;
                case Task::STATUS_SUCCESS:// 已完成
                case Task::STATUS_CLOSE:// 已关闭
                default:
                    $query->andWhere(['t.status' => $params['status']]);
                    break;
            }
        }

        $order = [
            't.created_at' => SORT_DESC,
            't.id' => SORT_DESC,
        ];

        $data = $this->getListPageRes($query, $page, $pageSize, $order, $directTotal);
        if ($directTotal == 1) {
            return $data;
        }

        // 格式化数据
        $data['list'] = $this->getListFormat($data['list']);
        $taskId = $data['list'][0]['id'] ?? 0;

        // 任务患者列表
        $data['patient_list'] = $this->getTaskPatientList([], 1, 10, $taskId);

        return $data;
    }

    /**
     * 任务患者列表
     *
     * @param array $params
     * @param int $page
     * @param int $pageSize
     * @param int $taskId
     * @return array
     */
    public function getTaskPatientList(array $params = [], int $page = 0, int $pageSize = 0, int $taskId = 0): array
    {
        $query = User::find()->alias('u')
            ->select([
                'u.*',
                'b.source_name',
                'd.outpatient_name',
                'e.name AS clinic_name',
                'f.realname AS belong_assistant_name',
                'm_member.realname AS belong_member_name',
                'g.diseases_name AS diseases_name',
                'h.diseases_name AS diseases_child_name',
                'tp.callback_id',
            ])
            ->innerJoin(TaskPatient::tableName().' tp', 'tp.patient_user_id = u.id')
            ->leftJoin(SourceChannel::tableName() . ' AS b', 'b.id = u.source_channel_id')
            ->leftJoin(OutpatientType::tableName() . ' AS d', 'd.id = u.outpatient_type_id')
            ->leftJoin(Clinic::tableName() . ' AS e', 'e.id = u.clinic_id')
            ->leftJoin(Member::tableName() . ' AS f', 'f.id = u.belong_assistant_id')
            ->leftJoin(Member::tableName() . ' m_member', 'm_member.id = u.belong_member_id')
            ->leftJoin(PatientDiseases::tableName() . ' AS g', 'g.id = u.patient_diseases_id')
            ->leftJoin(PatientDiseases::tableName() . ' AS h', 'h.id = u.patient_diseases_child_id')
            ->where([
                'tp.task_id' => $taskId
            ]);

        // 上次到诊时间
        if (isset($params['start_last_revisiting_time']) && $params['start_last_revisiting_time']) {
            $query->andWhere(['>=', 'u.last_revisiting_time', strtotime($params['start_last_revisiting_time'])]);
        }

        if (isset($params['end_last_revisiting_time']) && $params['end_last_revisiting_time']) {
            $query->andWhere(['<=', 'u.last_revisiting_time', strtotime($params['end_last_revisiting_time'].' 23:59:59')]);
        }

        // 关键词
        if (isset($params['keywords']) && $params['keywords']) {
            if (preg_match("/([\x81-\xfe][\x40-\xfe])/", $params['keywords'], $match) || preg_match("/(^[A-Za-z]+$)/",$params['keywords'])) {
                $query->andWhere(['like', 'u.name', "{$params['keywords']}%", false]);
            } else {
                $query->andWhere([
                    'or',
                    ['u.mobile' => trim($params['keywords'])],
                    ['u.patient_card_id' => trim($params['keywords'])],
                ]);
            }
        }

        $memberList = [];
        $type = 1;
        if (Yii::$app->params['is_assistant']) {
            $type = 2;
            $memberList = CommonService::getMemberList(2);
        } elseif (Yii::$app->params['is_customer']) {
            $memberList = CommonService::getMemberList(1);
        }
        if (!isset($params['belong_member_id']) || (isset($params['belong_member_id']) && empty($params['belong_member_id']))) {
            $params['belong_member_id'] = array_column($memberList, 'id');
        }

        // 归属人
        if (isset($params['belong_member_id'])) {
            if (Yii::$app->params['is_assistant']) {
                $query->andWhere(['u.belong_assistant_id' => $params['belong_member_id']]);
            } else {
                $query->andWhere(['u.belong_member_id' => $params['belong_member_id']]);
            }
        }

        // 就诊类型
        if (isset($params['outpatient_type_id']) && $params['outpatient_type_id']) {
            $query->andWhere(['u.outpatient_type_id' => $params['outpatient_type_id']]);
        }

        // 排序
        $orderBy = [];
        if (isset($params['sort_last_revisiting_type']) && $params['sort_last_revisiting_type']) {// 上次复诊
            if ($params['sort_last_revisiting_type'] == 1) {
                $orderBy = ['u.last_revisiting_time' => SORT_ASC];
            } elseif ($params['sort_last_revisiting_type'] == 2) {
                $orderBy = ['u.last_revisiting_time' => SORT_DESC];
            }
        } elseif (isset($params['sort_next_revisiting_type']) && $params['sort_next_revisiting_type']) {// 下次复诊
            if ($params['sort_next_revisiting_type'] == 1) {
                $orderBy = ['u.next_revisiting_time' => SORT_ASC];
            } elseif ($params['sort_next_revisiting_type'] == 2) {
                $orderBy = ['u.next_revisiting_time' => SORT_DESC];
            }
        } elseif (isset($params['sort_last_callback_type']) && $params['sort_last_callback_type']) {// 上次回访
            if ($type == 1) {
                if ($params['sort_last_callback_type'] == 1) {
                    $orderBy = ['u.kf_last_callback_time' => SORT_ASC];
                } elseif ($params['sort_last_callback_type'] == 2) {
                    $orderBy = ['u.kf_last_callback_time' => SORT_DESC];
                }
            } else {
                if ($params['sort_last_callback_type'] == 1) {
                    $orderBy = ['u.yz_last_callback_time' => SORT_ASC];
                } elseif ($params['sort_last_callback_type'] == 2) {
                    $orderBy = ['u.yz_last_callback_time' => SORT_DESC];
                }
            }

        } elseif (isset($params['sort_next_callback_type']) && $params['sort_next_callback_type']) {// 下次回访
            if ($type == 1) {
                if ($params['sort_next_callback_type'] == 1) {
                    $orderBy = ['u.kf_next_callback_time' => SORT_ASC];
                } elseif ($params['sort_next_callback_type'] == 2) {
                    $orderBy = ['u.kf_next_callback_time' => SORT_DESC];
                }
            } else {
                if ($params['sort_next_callback_type'] == 1) {
                    $orderBy = ['u.yz_next_callback_time' => SORT_ASC];
                } elseif ($params['sort_next_callback_type'] == 2) {
                    $orderBy = ['u.yz_next_callback_time' => SORT_DESC];
                }
            }
        } elseif (isset($params['sort_revisiting_type']) && $params['sort_revisiting_type']) {// 复诊次数
            if ($params['sort_revisiting_type'] == 1) {
                $orderBy = ['u.revisiting_count' => SORT_ASC];
            } elseif ($params['sort_revisiting_type'] == 2) {
                $orderBy = ['u.revisiting_count' => SORT_DESC];
            }
        } elseif (isset($params['sort_callback_type']) && $params['sort_callback_type']) {// 回访次数
            if ($params['sort_callback_type'] == 1) {
                $orderBy = ['u.callback_count' => SORT_ASC];
            } elseif ($params['sort_callback_type'] == 2) {
                $orderBy = ['u.callback_count' => SORT_DESC];
            }
        } elseif (isset($params['sort_missed_type']) && $params['sort_missed_type']) {// 未到诊天数
            if ($params['sort_missed_type'] == 1) {
                $orderBy = ['u.missed_days' => SORT_ASC];
            } elseif ($params['sort_missed_type'] == 2) {
                $orderBy = ['u.missed_days' => SORT_DESC];
            }
        } elseif (isset($params['sort_anti_type']) && $params['sort_anti_type']) {// 抗复发年限
            if ($params['sort_anti_type'] == 1) {
                $orderBy = ['u.anti_relapse_year' => SORT_ASC];
            } elseif ($params['sort_anti_type'] == 2) {
                $orderBy = ['u.anti_relapse_year' => SORT_DESC];
            }
        } else {
            $orderBy = [
                'tp.callback_id' => SORT_ASC,
                'u.created_at' => SORT_DESC,
            ];
        }
        $orderBy['u.id'] = SORT_DESC;

        $data = $this->getListPageRes($query, $page, $pageSize, $orderBy);

        // 格式化列表数据
        $service = new UserService();
        $data['list'] = $service->getListPageFormat($data['list'], $type);

        $data['condition'] = [
            'member_list' => $memberList,
            'outpatient_type_list' => CommonService::getOutpatientTypeList(),
        ];

        return $data;
    }

    /**
     * 统计任务总数
     *
     * @param int $status
     * @param int $total
     * @return array
     */
    public function getTaskTotalList(?int $status = 0, int $total = 0): array
    {
        $data = [
            'wait' => 0,
            'start' => 0,
            'delay' => 0,
            'success' => 0,
            'close' => 0,
        ];

        $params = ['status' => Task::STATUS_WAIT];
        if ($status !== Task::STATUS_WAIT) {
            $params['status'] = Task::STATUS_WAIT;
            $res = $this->getMyList($params, 0, 0, 1);
            $data['wait'] = $res['total'];
        } else {
            $data['wait'] = $total;
        }

        if ($status !== Task::STATUS_START) {
            $params['status'] = Task::STATUS_START;
            $res = $this->getMyList($params, 0, 0, 1);
            $data['start'] = $res['total'];
        } else {
            $data['start'] = $total;
        }

        if ($status !== Task::STATUS_DELAY) {
            $params['status'] = Task::STATUS_DELAY;
            $res = $this->getMyList($params, 0, 0, 1);
            $data['delay'] = $res['total'];
        } else {
            $data['delay'] = $total;
        }

        if ($status !== Task::STATUS_SUCCESS) {
            $params['status'] = Task::STATUS_SUCCESS;
            $res = $this->getMyList($params, 0, 0, 1);
            $data['success'] = $res['total'];
        } else {
            $data['success'] = $total;
        }

        if ($status !== Task::STATUS_CLOSE) {
            $params['status'] = Task::STATUS_CLOSE;
            $res = $this->getMyList($params, 0, 0, 1);
            $data['close'] = $res['total'];
        } else {
            $data['close'] = $total;
        }

        return $data;
    }

    /**
     * 新增任务
     *
     * @param array $params
     * @return array
     * @throws \Exception
     */
    public function create(array $params): array
    {
        $model = new TaskForm();
        $model->setScenario('create');
        $model->attributes = $params;
        if ($model->validate() === false) {
            throw new \Exception(CommonHelper::analyErr($model->getFirstErrors()));
        }

        $patientIds = $model->getPatientIds();
        $nowTime = strtotime(date('Y-m-d'));

        // 1.新增任务
        $task = new Task();
        $task->project_id = Yii::$app->params['project_id'];
        $task->title = $model->title;
        $task->start_time = strtotime($model->start_time);
        $task->end_time = strtotime($model->end_time);
        $task->total_num = count($patientIds);
        $task->status = $task->start_time <= $nowTime ? Task::STATUS_START : Task::STATUS_WAIT;
        $task->remark = $model->remark ?: '';
        $task->created_member_id = Yii::$app->user->identity->member_id;
        $task->saveAndCheckResult();

        // 2.新增任务患者筛选条件
        $taskCondition = new TaskCondition();
        $taskCondition->task_id = $task->id;
        $taskCondition->start_reg_time = $model->start_reg_time ? strtotime($model->start_reg_time) : 0;
        $taskCondition->end_reg_time = $model->end_reg_time ? strtotime($model->end_reg_time) : 0;
        $taskCondition->start_last_callback_time = $model->start_last_callback_time ? strtotime($model->start_last_callback_time) : 0;
        $taskCondition->end_last_callback_time = $model->end_last_callback_time ? strtotime($model->end_last_callback_time) : 0;
        $taskCondition->start_missed_days = $model->start_missed_days;
        $taskCondition->end_missed_days = $model->end_missed_days;
        $taskCondition->start_surplus_num = $model->start_surplus_num;
        $taskCondition->end_surplus_num = $model->end_surplus_num;
        $taskCondition->outpatient_type_ids = $model->getOutpatientTypeIds();
        $taskCondition->tag_ids = $model->getTagIds();
        $taskCondition->patient_diseases_ids = $model->getPatientDiseasesIds();
        $taskCondition->is_anti_relapse = $model->is_anti_relapse ?: 0;
        $taskCondition->saveAndCheckResult();

        // 3.添加任务人
        $memberIds = $model->getMemberIds();
        $columns = ['project_id', 'task_id', 'member_id', 'created_at', 'updated_at'];
        $batchMemberData = [];
        foreach ($memberIds as $row) {
            $batchMemberData[] = [
                Yii::$app->params['project_id'],
                $task->id,
                $row,
                Yii::$app->params['time'],
                Yii::$app->params['time'],
            ];
        }
        Yii::$app->db->createCommand()
            ->batchInsert(TaskMember::tableName(), $columns, $batchMemberData)
            ->execute();

        // 4.添加任务患者
        $patientIds = $model->getPatientIds();
        $columns = ['project_id', 'task_id', 'patient_user_id', 'created_at', 'updated_at'];
        $batchPatientData = [];
        foreach ($patientIds as $row) {
            $batchPatientData[] = [
                Yii::$app->params['project_id'],
                $task->id,
                $row,
                Yii::$app->params['time'],
                Yii::$app->params['time'],
            ];
        }
        Yii::$app->db->createCommand()
            ->batchInsert(TaskPatient::tableName(), $columns, $batchPatientData)
            ->execute();

        // 5.添加操作日志
        $content = $this->getOperateContent(
            null, $task,
            null, $taskCondition,
            [], $memberIds,
            [], $patientIds
        );
        $taskOperateLog = new TaskOperateLog();
        $taskOperateLog->task_id = $task->id;
        $taskOperateLog->content = $content;
        $taskOperateLog->type = TaskOperateLog::TYPE_CREATE;
        $taskOperateLog->operate_id = Yii::$app->user->identity->member_id;
        $taskOperateLog->saveAndCheckResult();

        if ($task->status == Task::STATUS_START) {
            $taskOperateLog = new TaskOperateLog();
            $taskOperateLog->task_id = $task->id;
            $taskOperateLog->content = "创建任务: 进行中";
            $taskOperateLog->type = TaskOperateLog::TYPE_START;
            $taskOperateLog->operate_id = Yii::$app->user->identity->member_id;
            $taskOperateLog->saveAndCheckResult();
        }

        return ['id' => $task->id];
    }

    /**
     * 编辑任务
     *
     * @param array $params
     * @return array
     * @throws \Exception
     */
    public function edit(array $params): array
    {
        $model = new TaskForm();
        $model->setScenario('edit');
        $model->attributes = $params;
        if ($model->validate() === false) {
            throw new \Exception(CommonHelper::analyErr($model->getFirstErrors()));
        }

        $patientIds = $model->getPatientIds();

        // 1.编辑任务
        $task = $model->getTaskInfo();
        $oldTask = $task->getAttributes();

        $task->title = $model->title;
        $task->start_time = strtotime($model->start_time);
        $task->end_time = strtotime($model->end_time);
        $task->total_num = count($patientIds);
        // 修复状态
        $task->status = $this->getTrueStatus((int)$task->status, $task->start_time, $task->end_time);
        $task->remark = $model->remark ?: '';
        $task->saveAndCheckResult();

        // 2.编辑任务患者筛选条件
        $taskCondition = TaskCondition::findOne(['task_id' => $task->id]);
        $oldTaskCondition = $taskCondition->getAttributes();

        $taskCondition->task_id = $task->id;
        $taskCondition->start_reg_time = $model->start_reg_time ? strtotime($model->start_reg_time) : 0;
        $taskCondition->end_reg_time = $model->end_reg_time ? strtotime($model->end_reg_time) : 0;
        $taskCondition->start_last_callback_time = $model->start_last_callback_time ? strtotime($model->start_last_callback_time) : 0;
        $taskCondition->end_last_callback_time = $model->end_last_callback_time ? strtotime($model->end_last_callback_time) : 0;
        $taskCondition->start_missed_days = $model->start_missed_days;
        $taskCondition->end_missed_days = $model->end_missed_days;
        $taskCondition->start_surplus_num = $model->start_surplus_num;
        $taskCondition->end_surplus_num = $model->end_surplus_num;
        $taskCondition->outpatient_type_ids = $model->getOutpatientTypeIds();
        $taskCondition->tag_ids = $model->getTagIds();
        $taskCondition->patient_diseases_ids = $model->getPatientDiseasesIds();
        $taskCondition->is_anti_relapse = $model->is_anti_relapse ?: 0;
        $taskCondition->saveAndCheckResult();

        // 3-1.删除原任务人
        $oldMemberIds = TaskMember::find()
            ->select(['member_id'])
            ->where(['task_id' => $task->id])
            ->column();
        TaskMember::deleteAll(['task_id' => $task->id]);

        // 3-2.添加任务人
        $memberIds = $model->getMemberIds();
        $columns = ['project_id', 'task_id', 'member_id', 'created_at', 'updated_at'];
        $batchMemberData = [];
        foreach ($memberIds as $row) {
            $batchMemberData[] = [
                Yii::$app->params['project_id'],
                $task->id,
                $row,
                Yii::$app->params['time'],
                Yii::$app->params['time'],
            ];
        }
        Yii::$app->db->createCommand()
            ->batchInsert(TaskMember::tableName(), $columns, $batchMemberData)
            ->execute();

        // 4-1.删除原任务患者
        $oldPatientIds = TaskPatient::find()
            ->select(['patient_user_id'])
            ->where(['task_id' => $task->id])
            ->column();
        TaskPatient::deleteAll(['task_id' => $task->id]);

        // 4-2.添加任务患者
        $patientIds = $model->getPatientIds();
        $columns = ['project_id', 'task_id', 'patient_user_id', 'created_at', 'updated_at'];
        $batchPatientData = [];
        foreach ($patientIds as $row) {
            $batchPatientData[] = [
                Yii::$app->params['project_id'],
                $task->id,
                $row,
                Yii::$app->params['time'],
                Yii::$app->params['time'],
            ];
        }
        Yii::$app->db->createCommand()
            ->batchInsert(TaskPatient::tableName(), $columns, $batchPatientData)
            ->execute();

        // 5.添加操作日志
        $content = $this->getOperateContent(
            $oldTask, $task,
            $oldTaskCondition, $taskCondition,
            $oldMemberIds, $memberIds,
            $oldPatientIds, $patientIds
        );
        if ($content) {
            $taskOperateLog = new TaskOperateLog();
            $taskOperateLog->task_id = $task->id;
            $taskOperateLog->content = $content;
            $taskOperateLog->type = TaskOperateLog::TYPE_EDIT;
            $taskOperateLog->operate_id = Yii::$app->user->identity->member_id;
            $taskOperateLog->saveAndCheckResult();
        }

        return ['id' => $task->id];
    }

    /**
     * 获取操作日志内容
     *
     * @param array|null $oldTask 原任务
     * @param Task|null $newTask 新任务
     * @param array|null $oldTaskCondition 原患者筛选条件
     * @param TaskCondition|null $newTaskCondition 新患者筛选条件
     * @param array $oldMemberIds 原任务人
     * @param array $newMemberIds 新任务人
     * @param array $oldPatientIds 原患者【预留】
     * @param array $newPatientIds 新患者【预留】
     * @return string
     */
    public function getOperateContent(
        ?array $oldTask, ?Task $newTask,
        ?array $oldTaskCondition, ?TaskCondition $newTaskCondition,
        array $oldMemberIds, array $newMemberIds,
        array $oldPatientIds, array $newPatientIds
    ): string
    {
        $content = '';
        $taskKey = ['title', 'start_time', 'end_time', 'total_num', 'remark'];
        $taskConditionKey = [
            'start_reg_time', 'end_reg_time', 'start_last_callback_time', 'end_last_callback_time',
            'start_missed_days', 'end_missed_days', 'start_surplus_num', 'end_surplus_num',
            'outpatient_type_ids', 'tag_ids', 'patient_diseases_ids', 'is_anti_relapse'
        ];

        foreach ($taskKey as $row) {
            $oldVal = $oldTask[$row] ?? '';
            $newVal = $newTask[$row] ?? '';

            if ($oldVal != $newVal) {
                switch ($row) {
                    case 'title':
                        $content .= "标题：【{$oldVal}】改为【{$newVal}】;";
                        break;
                    case 'start_time':
                        $oldVal = $oldVal ? date('Y-m-d', $oldVal) : '';
                        $newVal = $newVal ? date('Y-m-d', $newVal) : '';
                        $content .= "任务开始时间：【{$oldVal}】改为【{$newVal}】;";
                        break;
                    case 'end_time':
                        $oldVal = $oldVal ? date('Y-m-d', $oldVal) : '';
                        $newVal = $newVal ? date('Y-m-d', $newVal) : '';
                        $content .= "任务结束时间：【{$oldVal}】改为【{$newVal}】;";
                        break;
                    case 'total_num':
                        $content .= "患者总数：【{$oldVal}】改为【{$newVal}】;";
                        break;
                    case 'remark':
                        $content .= "备注：【{$oldVal}】改为【{$newVal}】;";
                        break;
                }
            }
        }

        foreach ($taskConditionKey as $row) {
            $oldVal = $oldTaskCondition[$row] ?? '';
            $newVal = $newTaskCondition[$row] ?? '';

            if ($oldVal != $newVal) {
                switch ($row) {
                    case 'start_reg_time':
                        $oldVal = $oldVal ? date('Y-m-d', $oldVal) : '';
                        $newVal = $newVal ? date('Y-m-d', $newVal) : '';
                        $content .= "挂号开始时间：【{$oldVal}】改为【{$newVal}】;";
                        break;
                    case 'end_reg_time':
                        $oldVal = $oldVal ? date('Y-m-d', $oldVal) : '';
                        $newVal = $newVal ? date('Y-m-d', $newVal) : '';
                        $content .= "挂号结束时间：【{$oldVal}】改为【{$newVal}】;";
                        break;
                    case 'start_last_callback_time':
                        $oldVal = $oldVal ? date('Y-m-d', $oldVal) : '';
                        $newVal = $newVal ? date('Y-m-d', $newVal) : '';
                        $content .= "上次回访开始时间：【{$oldVal}】改为【{$newVal}】;";
                        break;
                    case 'end_last_callback_time':
                        $oldVal = $oldVal ? date('Y-m-d', $oldVal) : '';
                        $newVal = $newVal ? date('Y-m-d', $newVal) : '';
                        $content .= "上次回访结束时间：【{$oldVal}】改为【{$newVal}】;";
                        break;
                    case 'start_missed_days':
                        $content .= "未到天数最小值：【{$oldVal}】改为【{$newVal}】;";
                        break;
                    case 'end_missed_days':
                        $content .= "未到天数最大值：【{$oldVal}】改为【{$newVal}】;";
                        break;
                    case 'start_surplus_num':
                        $content .= "308剩余量最小值：【{$oldVal}】改为【{$newVal}】;";
                        break;
                    case 'end_surplus_num':
                        $content .= "308剩余量最大值：【{$oldVal}】改为【{$newVal}】;";
                        break;
                    case 'outpatient_type_ids':
                        $oldVal = $oldVal ?: [];
                        $newVal = $newVal ?: [];
                        if ($oldVal != $newVal) {
                            /**
                             * @var OutpatientType[] $outpatientTypeList
                             */
                            $outpatientTypeList = OutpatientType::find()
                                ->where(['id' => array_merge($oldVal, $newVal)])
                                ->all();
                            $oldValArr = $newValArr = [];
                            foreach ($outpatientTypeList as $outpatientType) {
                                if (in_array($outpatientType->id, $oldVal)) {
                                    $oldValArr[] = $outpatientType->outpatient_name;
                                }

                                if (in_array($outpatientType->id, $newVal)) {
                                    $newValArr[] = $outpatientType->outpatient_name;
                                }
                            }
                            $oldVal = implode(',', $oldValArr);
                            $newVal = implode(',', $newValArr);
                            $content .= "就诊类型：【{$oldVal}】改为【{$newVal}】;";
                        }
                        break;
                    case 'tag_ids':
                        $oldVal = $oldVal ?: [];
                        $newVal = $newVal ?: [];
                        if ($oldVal != $newVal) {
                            /**
                             * @var Tags[] $tagList
                             */
                            $tagList = Tags::find()
                                ->where(['id' => array_merge($oldVal, $newVal)])
                                ->all();
                            $oldValArr = $newValArr = [];
                            foreach ($tagList as $tag) {
                                if (in_array($tag->id, $oldVal)) {
                                    $oldValArr[] = $tag->tags_name;
                                }

                                if (in_array($tag->id, $newVal)) {
                                    $newValArr[] = $tag->tags_name;
                                }
                            }
                            $oldVal = implode(',', $oldValArr);
                            $newVal = implode(',', $newValArr);
                            $content .= "标签：【{$oldVal}】改为【{$newVal}】;";
                        }
                        break;
                    case 'patient_diseases_ids':
                        $oldVal = $oldVal ?: [];
                        $newVal = $newVal ?: [];
                        if ($oldVal != $newVal) {
                            /**
                             * @var PatientDiseases[] $patientDiseasesList
                             */
                            $patientDiseasesList = PatientDiseases::find()
                                ->where(['id' => array_merge($oldVal, $newVal)])
                                ->all();
                            $oldValArr = $newValArr = [];
                            foreach ($patientDiseasesList as $patientDiseases) {
                                if (in_array($patientDiseases->id, $oldVal)) {
                                    $oldValArr[] = $patientDiseases->diseases_name;
                                }

                                if (in_array($patientDiseases->id, $newVal)) {
                                    $newValArr[] = $patientDiseases->diseases_name;
                                }
                            }
                            $oldVal = implode(',', $oldValArr);
                            $newVal = implode(',', $newValArr);
                            $content .= "病种：【{$oldVal}】改为【{$newVal}】;";
                        }
                        break;
                    case 'is_anti_relapse':
                        $oldVal = $oldVal ? '是' : '全部';
                        $newVal = $newVal ? '是' : '全部';
                        $content .= "抗复发：【{$oldVal}】改为【{$newVal}】;";
                        break;
                }
            }
        }

        if ($oldMemberIds != $newMemberIds) {
            /**
             * @var Member[] $memberList
             */
            $memberList = Member::find()
                ->where(['id' => array_merge($oldMemberIds, $newMemberIds)])
                ->all();
            $oldValArr = $newValArr = [];
            foreach ($memberList as $member) {
                if (in_array($member->id, $oldMemberIds)) {
                    $oldValArr[] = $member->realname;
                }

                if (in_array($member->id, $newMemberIds)) {
                    $newValArr[] = $member->realname;
                }
            }
            $oldVal = implode(',', $oldValArr);
            $newVal = implode(',', $newValArr);
            $content .= "任务人：【{$oldVal}】改为【{$newVal}】;";
        }

        return $content;
    }

    /**
     * 关闭任务
     *
     * @param array $params
     * @return array
     * @throws \Exception
     */
    public function close(array $params): array
    {
        $model = new TaskForm();
        $model->setScenario('close');
        $model->attributes = $params;
        if ($model->validate() === false) {
            throw new \Exception(CommonHelper::analyErr($model->getFirstErrors()));
        }

        // 1.编辑任务
        $task = $model->getTaskInfo();
        $task->status = Task::STATUS_CLOSE;
        $task->reason = $model->reason;
        $task->saveAndCheckResult();

        // 2.添加操作日志
        $taskOperateLog = new TaskOperateLog();
        $taskOperateLog->task_id = $task->id;
        $taskOperateLog->content = "任务关闭: {$task->reason}";
        $taskOperateLog->type = TaskOperateLog::TYPE_CLOSE;
        $taskOperateLog->operate_id = Yii::$app->user->identity->member_id;
        $taskOperateLog->saveAndCheckResult();

        return ['id' => $task->id];
    }

    /**
     * 删除任务
     *
     * @param array $params
     * @return array
     * @throws \Exception
     */
    public function delete(array $params): array
    {
        $model = new TaskForm();
        $model->setScenario('delete');
        $model->attributes = $params;
        if ($model->validate() === false) {
            throw new \Exception(CommonHelper::analyErr($model->getFirstErrors()));
        }

        // 1.编辑任务
        $task = $model->getTaskInfo();
        $task->status = Task::STATUS_DELETE;
        $task->saveAndCheckResult();

        // 2.添加操作日志
        $taskOperateLog = new TaskOperateLog();
        $taskOperateLog->task_id = $task->id;
        $taskOperateLog->content = "任务删除";
        $taskOperateLog->type = TaskOperateLog::TYPE_DELETE;
        $taskOperateLog->operate_id = Yii::$app->user->identity->member_id;
        $taskOperateLog->saveAndCheckResult();

        return ['id' => $task->id];
    }

    /**
     * 设置任务提醒
     *
     * @param array $params
     * @return array
     * @throws \Exception
     */
    public function remind(array $params): array
    {
        $model = new TaskForm();
        $model->setScenario('remind');
        $model->attributes = $params;
        if ($model->validate() === false) {
            throw new \Exception(CommonHelper::analyErr($model->getFirstErrors()));
        }

        $task = $model->getTaskInfo();

        // 1.添加任务提醒
        $taskRemind = TaskRemind::findOne(['task_id' => $task->id]);
        $oldTaskRemind = $taskRemind ? $taskRemind->getAttributes() : [];
        if (empty($taskRemind)) {
            $taskRemind = new TaskRemind();
            $taskRemind->task_id = $task->id;
        }
        $taskRemind->access_token = $model->access_token;
        $taskRemind->secret = $model->secret ?: '';
        $taskRemind->content = $model->getRemindContent();
        $taskRemind->saveAndCheckResult();

        // 2.添加操作日志
        $content = '';
        if ($oldTaskRemind) {
            if ($oldTaskRemind['access_token'] != $taskRemind->access_token) {
                $content .= "钉钉token：【{$oldTaskRemind['access_token']}】改为【{$taskRemind->access_token}】;";
            }

            if ($oldTaskRemind['secret'] != $taskRemind->secret) {
                $content .= "钉钉secret：【{$oldTaskRemind['secret']}】改为【{$taskRemind->secret}】;";
            }

            if ($oldTaskRemind['content'] != $taskRemind->content) {
                $oldVal = $this->getRemindContentStr($oldTaskRemind['content']);
                $newVal = $this->getRemindContentStr($taskRemind->content);
                $content .= "内容：【{$oldVal}】改为【{$newVal}】;";
            }
        } else {
            $content = "钉钉token：【】改为【{$taskRemind->access_token}】;";
            if ($taskRemind->secret) {
                $content .= "钉钉secret：【】改为【{$taskRemind->secret}】;";
            }
            $newVal = $this->getRemindContentStr($taskRemind->content);
            $content .= "内容：【】改为【{$newVal}】;";
        }
        if ($content) {
            $taskOperateLog = new TaskOperateLog();
            $taskOperateLog->task_id = $task->id;
            $taskOperateLog->content = $content;
            $taskOperateLog->type = TaskOperateLog::TYPE_REMIND;
            $taskOperateLog->operate_id = Yii::$app->user->identity->member_id;
            $taskOperateLog->saveAndCheckResult();
        }

        return ['id' => $task->id];
    }

    /**
     * 发送提醒
     *
     * @param int $taskId
     * @param int $status
     * @return bool
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function taskRemind(int $taskId, int $status)
    {
        try {
            $taskRemind = TaskRemind::findOne(['task_id' => $taskId]);
            if (empty($taskRemind)) {
                return false;
            }

            if (!isset($taskRemind->content[$status])) {
                return false;
            }

            $task = Task::findOne($taskId);

            $data = [
                'msgtype' => 'markdown',
                'markdown_data' => [
                    'title' => $task->title,
                    'data_arr' => [
                        Task::$statuss[$status] => $taskRemind->content[$status],
                    ],
                ],
            ];

            $res = DingHelper::sendMessage($taskRemind->access_token, $taskRemind->secret, $data);

            if ($res['errcode'] != 0) {
                return false;
            }

            return true;
        } catch (\Exception $e) {
            return false;
        }
    }

    /**
     * 获取提醒内容
     *
     * @param array $contentArr
     * @return string
     */
    public function getRemindContentStr(array $contentArr): string
    {
        $content = '';
        foreach ($contentArr as $key => $row) {
            $statusStr = Task::$statuss[$key];
            $content .= "{$statusStr}：{$row};";
        }

        return $content;
    }

    /**
     * 定时任务提醒【进行中、已延期】
     *
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function cronTaskRemind()
    {
        set_time_limit(0);
        ini_set('memory_limit', '2G');

        $taskList = Task::find()->alias('t')
            ->select([
                't.id', 't.title', 't.status', 't.start_time', 't.end_time',
                'tr.access_token', 'tr.secret', 'tr.content',
            ])
            ->innerJoin(TaskRemind::tableName().' tr', 'tr.task_id = t.id')
            ->where([
                'status' => [Task::STATUS_WAIT, Task::STATUS_START, Task::STATUS_DELAY]
            ])
            ->orderBy(['t.id' => SORT_ASC])
            ->asArray()
            ->all();

        foreach ($taskList as $row) {
            $status = $this->getTrueStatus((int)$row['status'], $row['start_time'], $row['end_time']);

            $content = json_decode($row['content'], true);
            if (!isset($content[$status])) {
                continue;
            }

            try {
                $data = [
                    'msgtype' => 'markdown',
                    'markdown_data' => [
                        'title' => $row['title'],
                        'data_arr' => [
                            Task::$statuss[$status] => $content[$status],
                        ],
                    ],
                ];

                $res = DingHelper::sendMessage($row['access_token'], $row['secret'], $data);
                if ($res['errcode'] != 0) {
                    echo $res['errmsg'] . PHP_EOL;
                }
            } catch (\Exception $e) {
                echo "error: ".$e->getMessage().PHP_EOL;
            }
        }
    }

    /**
     * 更新任务回访
     *
     * @param int $patientUserId
     * @param int $callbackId
     * @return array
     */
    public function updateCallbackTask(int $patientUserId = 0, int $callbackId = 0): array
    {
        // 1.获取未回访患者 && 进行中/已延迟任务
        $taskList = TaskPatient::find()->alias('tp')
            ->select(['t.*'])
            ->innerJoin(Task::tableName().' t', 't.id = tp.task_id')
            ->where([
                'tp.patient_user_id' => $patientUserId,
                'tp.callback_id' => 0,
                't.project_id' => Yii::$app->params['project_id'],
            ])
            ->andWhere([
                'or',
                [
                    'and',
                    ['t.status' => [Task::STATUS_WAIT, Task::STATUS_START]],
                    ['<=', 't.start_time', Yii::$app->params['time']],
                    ['>=', 't.end_time', Yii::$app->params['time'] - 86400],
                ],
                [
                    'and',
                    ['t.status' => [Task::STATUS_WAIT, Task::STATUS_START, Task::STATUS_DELAY]],
                    ['<', 't.end_time', Yii::$app->params['time'] - 86400],
                ]
            ])
            ->asArray()
            ->all();

        // 2.任务人
        $taskMemberList = TaskMember::find()
            ->where(['task_id' => array_column($taskList, 'id')])
            ->asArray()
            ->all();
        $taskMemberListFormat = [];
        foreach ($taskMemberList as $row) {
            $taskMemberListFormat[$row['task_id']][] = $row['member_id'];
        }

        $taskIds = [];
        foreach ($taskList as $row) {
            // 状态修复为定义状态
            $status = $this->getTrueStatus((int)$row['status'], $row['start_time'], $row['end_time']);

            // 1.更新任务
            $data = [
                'callback_num' => $row['callback_num'] + 1,
            ];
            if ($data['callback_num'] == $row['total_num']) {
                $data['status'] = Task::STATUS_SUCCESS;

                $taskIds[] = $row['id'];
            } else {
                if ($status != $row['status']) {
                    $data['status'] = $status;
                }
            }
            Task::updateAll($data, ['id' => $row['id']]);

            // 2.更新任务患者
            TaskPatient::updateAll(
                ['callback_id' => $callbackId],
                ['task_id' => $row['id'], 'patient_user_id' => $patientUserId]
            );

            // 3.添加日志
            if (isset($data['status']) && $data['status'] == Task::STATUS_SUCCESS) {
                $taskOperateLog = new TaskOperateLog();
                $taskOperateLog->task_id = $row['id'];
                $taskOperateLog->content = "任务完成";
                $taskOperateLog->type = TaskOperateLog::TYPE_SUCCESS;
                $taskOperateLog->operate_id = 0;
                $taskOperateLog->saveAndCheckResult();
            }
        }

        return $taskIds;
    }

    /**
     * 定时任务系统日志
     *
     * @throws \yii\db\Exception
     */
    public function cronUpdateTask()
    {
        set_time_limit(0);
        ini_set('memory_limit', '2G');

        $taskList = Task::find()
            ->select(['*'])
            ->andWhere([
                'or',
                [
                    'and',
                    ['status' => [Task::STATUS_WAIT, Task::STATUS_START]],
                    ['<=', 'start_time', Yii::$app->params['time']],
                    ['>=', 'end_time', Yii::$app->params['time'] - 86400],
                ],
                [
                    'and',
                    ['status' => [Task::STATUS_WAIT, Task::STATUS_START, Task::STATUS_DELAY]],
                    ['<', 'end_time', Yii::$app->params['time'] - 86400],
                ]
            ])
            ->indexBy('id')
            ->asArray()
            ->all();

        $columns = ['task_id', 'content', 'type', 'operate_id', 'created_at', 'updated_at'];
        $batchData = [];
        foreach ($taskList as $row) {
            $status = $this->getTrueStatus((int)$row['status'], $row['start_time'], $row['end_time']);

            // 1.更新任务状态
            if ($status != $row['status']) {
                Task::updateAll(['status' => $status], ['id' => $row['id']]);

                $batchData[] = [
                    $row['id'],
                    $status == Task::STATUS_START ? '任务进行中' : '任务已延期',
                    $status == Task::STATUS_START ? TaskOperateLog::TYPE_START : TaskOperateLog::TYPE_DELAY,
                    0,
                    Yii::$app->params['time'],
                    Yii::$app->params['time'],
                ];
            }
        }

        // 2.统计昨日进度
        $taskIds = array_column($taskList, 'id');

        // 任务人
        $taskMemberList = TaskMember::find()
            ->where([
                'task_id' => $taskIds
            ])
            ->asArray()
            ->all();
        $taskMemberListFormat = [];
        foreach ($taskMemberList as $row) {
            $taskMemberListFormat[$row['task_id']][] = $row['member_id'];
        }

        // 回访患者
        $taskPatientList = TaskPatient::find()->alias('tp')
            ->select([
                'tp.*',
                'cb.current_callback_time', 'cb.member_id', 'cb.member_name',
            ])
            ->innerJoin(Callback::tableName().' cb', 'cb.id = tp.callback_id')
            ->where([
                'task_id' => $taskIds
            ])
            ->asArray()
            ->all();
        $nowTime = strtotime(date('Y-m-d'));
        $yesterTime = $nowTime - 86400;

        $taskPatientListFormat = [];
        $attrFormat = [];
        foreach ($taskPatientList as $row) {
            $taskPatientListFormat[$row['task_id']] = $taskPatientListFormat[$row['task_id']]
                ?? ['member_list' => [], 'add_num' => 0];

            // 昨日增长数
            if ($row['current_callback_time'] >= $yesterTime && $row['current_callback_time'] < $nowTime) {
                $taskPatientListFormat[$row['task_id']]['add_num']++;
            }

            $taskMemberIds = $taskMemberListFormat[$row['task_id']] ?? [];
            // 是否为任务人
            if (!in_array($row['member_id'], $taskMemberIds)) {
                continue;
            }

            if (!isset($taskPatientListFormat[$row['task_id']]['member_list'][$row['member_id']])) {
                $attrFormat[$row['project_id']] = $attrFormat[$row['project_id']] ?? [];
                if (isset($attrFormat[$row['project_id']][$row['member_id']])) {
                    $attr = $attrFormat[$row['project_id']][$row['member_id']];
                } else {
                    $attr = $attrFormat[$row['project_id']][$row['member_id']] = Attr::findOne([
                        'project_id' => $row['project_id'],
                        'member_id' => $row['member_id']
                    ]);
                }

                $taskPatientListFormat[$row['task_id']]['member_list'][$row['member_id']] = [
                    'profession_name' => $attr->profession_name ?? '',
                    'member_name' => $row['member_name'],
                    'callback_num' => 0,
                ];
            }

            // 统计任务人回访数
            $taskPatientListFormat[$row['task_id']]['member_list'][$row['member_id']]['callback_num']++;
        }

        foreach ($taskPatientListFormat as $key => $row) {
            $content = '';

            foreach ($row['member_list'] as $val) {
                if (empty($val['callback_num'])) {
                    continue;
                }

                $content .= "{$val['profession_name']}-{$val['member_name']} 已回访：{$val['callback_num']};";
            }

            $total = $taskList[$key]['total_num'] ?? 0;
            $addRate = number_format($row['add_num'] / $total * 100, 2, '.', '');
            $content .= "  进度新增：{$addRate}%";

            $batchData[] = [
                $key,
                $content,
                TaskOperateLog::TYPE_SYS_SCHEDULE,
                0,
                Yii::$app->params['time'],
                Yii::$app->params['time'],
            ];
        }

        Yii::$app->db->createCommand()
            ->batchInsert(TaskOperateLog::tableName(), $columns, $batchData)
            ->execute();
    }
}
